package mosdi.util.iterators;

import java.util.Arrays;
import java.util.NoSuchElementException;

import mosdi.util.BitArray;

public class GeneralizedStringInstanceIterator extends LexicographicalIterator {

	private BitArray[] generalizedString;
	private LexicographicalIterator iterator;
	private int leftmostChangedPosition;
	private int[] next;
	private int nextLeftmostChangedPosition;

	public GeneralizedStringInstanceIterator(int alphabetSize, BitArray[] generalizedString) {
		for (BitArray g : generalizedString) {
			if (g.size()!=alphabetSize) throw new IllegalArgumentException();
		}
		this.generalizedString = generalizedString;
		this.iterator = new StringIterator(alphabetSize, generalizedString.length);
		step();
	}

	public GeneralizedStringInstanceIterator(int alphabetSize, int[] generalizedString, BitArray[] generalizedAlphabet) {
		for (BitArray g : generalizedAlphabet) {
			if (g.size()!=alphabetSize) throw new IllegalArgumentException();
		}
		this.generalizedString = new BitArray[generalizedString.length];
		for (int i=0; i<generalizedString.length; ++i) {
			this.generalizedString[i] = generalizedAlphabet[generalizedString[i]];
		}
		this.iterator = new StringIterator(alphabetSize, generalizedString.length);
		step();
	}
	
	private void step() {
		nextLeftmostChangedPosition = generalizedString.length;
		next = null;
		for (;iterator.hasNext(); next=null) {
			next = iterator.next();
			int i = iterator.getLeftmostChangedPosition();
			nextLeftmostChangedPosition = Math.min(nextLeftmostChangedPosition, i);
			for (; i<next.length; ++i) {
				if (!generalizedString[i].get(next[i])) {
					iterator.skip(i);
					break;
				}
			}
			if (i==next.length) break;
		}
	}
	
	@Override
	public boolean hasNext() {
		return next!=null;
	}

	@Override
	/** Returns strings over the IUPAC alphabet as given by Alphabet.getIupacAlphabet(). */
	public int[] next() {
		if (next==null) throw new NoSuchElementException();
		int[] result = next;
		leftmostChangedPosition = nextLeftmostChangedPosition;
		step();
		return result;
	}

	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}

	@Override
	public int getLeftmostChangedPosition() {
		return leftmostChangedPosition;
	}

	@Override
	public int getStringLength() {
		return generalizedString.length;
	}

	@Override
	/** Returns strings over the IUPAC alphabet as given by Alphabet.getIupacAlphabet(). */
	public int[] peek() {
		if (next==null) throw new NoSuchElementException();
		return Arrays.copyOf(next, next.length);
	}

	@Override
	public void skip(int position) {
		iterator.skip(position);
		step();
	}

}
